﻿//=======================================================
// Author: Jonas Follesø <jonas@follesoe.no>
// Copyright (c) 2008
//
// Licensed under the Microsoft Public License (Ms-PL)
// A copy of the license is available at 
// http://www.opensource.org/licenses/ms-pl.html
//=======================================================

using System;
using System.Windows;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Media.Imaging;
using System.Windows.Interop;

using Microsoft.Practices.Composite.Wpf.Commands;

using Colorful.Control.Services;

namespace Colorful.Control
{
    public class ColorfulViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        
        private bool isBussy;
        private int itemsPerPage;
        private int currentPageIndex;
        private int totalNumberOfItems;
        private string searchQuery;
        private string statusMessage;
        private string errorMessage;
        private ColorList currentList;
        private ObservableCollection<Palette> palettes;

        private IGetColors colorService;
        private ISaveColors saveService;
        private ClipboardManager clipboard;
        private BackgroundWorker worker;

        public DelegateCommand<object> ConfigureProxyCommand { get; private set; }
        public DelegateCommand<object> NextPageCommand { get; private set; }
        public DelegateCommand<object> PreviousPageCommand { get; private set; }
        public DelegateCommand<object> ExecuteSearchCommand { get; private set; }
        public DelegateCommand<Palette> ViewInBrowserCommand { get; private set; }
        public DelegateCommand<Palette> CopyToClipboardCommand { get; private set; }
        public DelegateCommand<Palette> SaveAsDesignSwatchCommand { get; private set; }

        public BitmapSource Logo
        {
            get
            {
                return Imaging.CreateBitmapSourceFromHBitmap(colorService.Logo.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            }
        }

        public bool IsBussy
        {
            get { return isBussy; }
            set
            {                
                if (isBussy != value)
                {
                    isBussy = value;
                    RaisePropertyChanged("IsBussy");
                    RaisePropertyChanged("IsEnabled");
                    RaisePropertyChanged("IsSearchEnabled");
                    NextPageCommand.RaiseCanExecuteChanged();
                    PreviousPageCommand.RaiseCanExecuteChanged();
                    ExecuteSearchCommand.RaiseCanExecuteChanged();
                }
            }
        }

        public bool IsEnabled
        {
            get { return !IsBussy; }            
        }

        public bool IsSearchEnabled
        {
            get { return CurrentList == ColorList.Search; }
        }

        public string ErrorMessage
        {
            get { return errorMessage; }
            set
            {
                if (errorMessage != value)
                {
                    errorMessage = value;
                    RaisePropertyChanged("ErrorMessage");
                }
            }
        }

        public string SearchQuery
        {
            get { return searchQuery; }
            set
            {
                if (searchQuery != value)
                {
                    searchQuery = value;
                    RaisePropertyChanged("SearchQuery");
                }
            }
        }

        public string StatusMessage
        {
            get { return statusMessage; }
            set
            {
                if (statusMessage != value)
                {
                    statusMessage = value;
                    RaisePropertyChanged("StatusMessage");
                }
            }
        }
        
        public ObservableCollection<Palette> Palettes
        {
            get { return palettes; }
            set
            {
                if (palettes != value)
                {
                    palettes = value;
                    RaisePropertyChanged("Palettes");
                }
            }
        }

        public IEnumerable<ColorList> AvailableLists
        {
            get
            {
                return colorService.SupportedLists;
            }
        }

        public ColorList CurrentList
        {
            get { return currentList; }
            set 
            {
                if (currentList != value)
                {
                    currentList = value;
                    CurrentPageIndex = 0;                    
                    RaisePropertyChanged("CurrentList");
                    RaisePropertyChanged("IsSearchEnabled");
                    ExecuteSearchCommand.RaiseCanExecuteChanged();
                    if (value != ColorList.Search)
                    {
                        ExecuteSearchCommand.Execute(null);
                    }
                }
            }
        }



        public int ItemsPerPage
        {
            get { return itemsPerPage; }
            set
            {
                if (itemsPerPage != value)
                {
                    itemsPerPage = value;
                    RaisePropertyChanged("ItemsPerPage");
                }
            }
        }

        public int CurrentPageIndex
        {
            get { return currentPageIndex; }
            set
            {
                if (currentPageIndex != value)
                {
                    currentPageIndex = value;
                    RaisePropertyChanged("CurrentPageIndex");
                    NextPageCommand.RaiseCanExecuteChanged();
                    PreviousPageCommand.RaiseCanExecuteChanged();
                }
            }
        }

        public int TotalNumberOfItems
        {
            get { return totalNumberOfItems; }
            set
            {
                if (totalNumberOfItems != value)
                {
                    totalNumberOfItems = value;
                    RaisePropertyChanged("TotalNumberOfItems");
                }
            }
        }
        
        public int TotalNumberOfPages
        {
            get
            {
                return Convert.ToInt32(Math.Ceiling(TotalNumberOfItems / (double) ItemsPerPage));
            }
        }

        public ColorfulViewModel()
        {
            colorService = new DesignTimeColorServiceStub();
            clipboard = new ClipboardManager(new XamlTextClipboardFormatter());            
            Initialize();
            ExecuteSearchCommand.Execute(null);
        }        

        public ColorfulViewModel(IGetColors colorService, params ICopyColors[] formatters)
        {            
            this.colorService = colorService;
            this.clipboard = new ClipboardManager(formatters);            
            Initialize();
        }

        private void Initialize()
        {
            worker = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = true};
            worker.DoWork += DoWork;
            worker.RunWorkerCompleted += WorkCompleted;

            saveService = new ExpressionDesignSwatchSaver();

            ConfigureProxyCommand = new DelegateCommand<object>(ConfigureProxyExecuted, o => true ); 
            NextPageCommand = new DelegateCommand<object>(NextPageExecuted, NextPageCanExecute);
            PreviousPageCommand = new DelegateCommand<object>(PreviousPageExecuted, PreviousPageCanExecute);
            ExecuteSearchCommand = new DelegateCommand<object>(ExecuteSearchExecuted, ExecuteSearchCanExecute);

            ViewInBrowserCommand = new DelegateCommand<Palette>(ViewInBrowserExecuted);
            CopyToClipboardCommand = new DelegateCommand<Palette>(CopyToClipboardExecuted);
            SaveAsDesignSwatchCommand = new DelegateCommand<Palette>(SaveAsDesignSwatchExecuted);            

            this.itemsPerPage = 15;
            this.currentPageIndex = 0;
            this.currentList = ColorList.Recent;
            this.Palettes = new ObservableCollection<Palette>();
            this.StatusMessage = "Loading Colors...";
        }

        private void ConfigureProxyExecuted( object obj )
        {
            var cfgWindow = new ConfigureProxyWindow
                                {
                                    WindowStartupLocation = WindowStartupLocation.CenterScreen,
                                    DataContext = new ConfigureProxyViewModel()
                                };

            if(!(cfgWindow.ShowDialog() ?? false))
                return;

            ExecuteWorker();
        }

        public void SwitchProvider()
        {
            if (colorService is AdobeKuler)
                colorService = new ColourLovers();
            else
                colorService = new AdobeKuler();

            RaisePropertyChanged("Logo");
            RaisePropertyChanged("LogoTooltip");
            ExecuteWorker();
        }

        public DataObject CreateDragDropObject(Palette palette)
        {
            return clipboard.CreateDragDropObject(palette);
        }

        private static void ViewInBrowserExecuted(Palette palette)
        {
            System.Diagnostics.Process.Start(palette.ThemeUrl);   
        }

        private void CopyToClipboardExecuted(Palette palette)            
        {
            clipboard.AddToClipboard(palette);            
        }

        private void SaveAsDesignSwatchExecuted(Palette palette)
        {
            saveService.Save(palette);
        }

        private void NextPageExecuted(object arg)
        {
            StatusMessage = "Loading Colors...";
            CurrentPageIndex++;            
            ExecuteWorker();
        }

        private bool NextPageCanExecute(object arg)
        {
            return (CurrentPageIndex + 1) * ItemsPerPage  < TotalNumberOfItems && !IsBussy;
        }

        private void PreviousPageExecuted(object arg)
        {
            StatusMessage = "Loading Colors...";
            CurrentPageIndex--;            
            ExecuteWorker();
        }

        private bool PreviousPageCanExecute(object arg)
        {
            return CurrentPageIndex > 0 && !IsBussy;
        }

        private void ExecuteSearchExecuted(object arg)
        {
            StatusMessage = CurrentList == ColorList.Search ? "Searching..." : "Loading Colors...";
            ExecuteWorker();
        }

        private bool ExecuteSearchCanExecute(object arg)
        {
            return IsSearchEnabled;
        }

        private void ExecuteWorker()
        {
            IsBussy = true;
            if (worker.IsBusy) worker.CancelAsync();
            else worker.RunWorkerAsync();
        }

        private void DoWork(object sender, DoWorkEventArgs e)
        {     
            int totalItems = 0;
            List<Palette> result = null;
            Exception error = null;
            Thread t;
            if (CurrentList == ColorList.Search)
            {
                t = new Thread(() => 
                    {
                        try
                        {
                            result = colorService.Search(SearchQuery, CurrentPageIndex, ItemsPerPage, out totalItems);
                        }
                        catch (Exception ex)
                        {
                            error = ex;
                        }
                    });
            }
            else
            {
                t = new Thread(() =>
                    {
                        try
                        {
                            result = colorService.GetColors(CurrentList, CurrentPageIndex, ItemsPerPage, out totalItems);
                        }
                        catch (Exception ex)
                        {
                            error = ex;
                        }
                    });
            }

            t.Name = "Color Download Thread";
            t.IsBackground = true;
            t.Start();

            while (result == null && error == null)
            {
                if (worker.CancellationPending)
                {
                    t.Abort();
                    e.Cancel = true;                    
                    return;
                }
                Thread.Sleep(25);
            }

            if (error != null)
                throw new ApplicationException("An exception occurred when trying to download colors", error);

            e.Result = new KeyValuePair<int, List<Palette>>(totalItems, result);
        }

        private void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled && e.Error == null && e.Result != null)
            {
                var result = (KeyValuePair<int, List<Palette>>)e.Result;

                Palettes.Clear();
                foreach (var palette in result.Value)
                    Palettes.Add(palette);

                TotalNumberOfItems = result.Key;                
                StatusMessage = string.Format("{0} Results ({1} of {2})", TotalNumberOfItems, CurrentPageIndex + 1, TotalNumberOfPages);
                ErrorMessage = null;
                IsBussy = false;
            }
            else if (e.Error != null)
            {
                ErrorMessage = e.Error.InnerException.ToString();
                StatusMessage = "Error occurred";
                IsBussy = false;
            }
            else if (e.Cancelled)
            {
                worker.RunWorkerAsync();
            }
        }

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}